package com.hp.usage.gr.authorize;

import com.hp.siu.collector.rules.Rule;
import com.hp.siu.collector.rules.RuleChain;
import com.hp.siu.collector.rules.RuleException;
import com.hp.siu.logging.Logger;
import com.hp.siu.naming.IUMContextHelper;
import com.hp.siu.serverutils.ServerInfo;
import com.hp.siu.sessionserver.RuleChainManager;
import com.hp.siu.utils.*;
import com.hp.usage.gr.cache.EHCacheService;
import com.hp.usage.gr.db.DbBasedRule;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

import javax.naming.CompoundName;
import javax.naming.InitialContext;
import java.util.Map;
import java.util.Properties;

/**
 * Created by huxiaoji on 1/18/2016.
 */
public class GrAuthorization extends Rule {
    public static final long RESULT_CODE_NOT_INITED = 3000;
    public static final long RESULT_CODE_NO_STATUS_RECORD = 3001;
    public static final long RESULT_CODE_INVALID_INFO = 3002;
    public static final long RESULT_CODE_INVALID_SITE_INFO = 3003;
    public static final long RESULT_CODE_LOGIN_FAILED = 3004;

    private static Logger log_ = Logger.getLogger("com.hp.usage.gr.authorize.GrAuthorization", "com.hp.usage.gr.message_catalog");

    private String unauthorizedRule;
    private String authorizationUserAttribute;
    private String authorizationPasswordAttribute;
    private String uninitializedRule;
    private String notReadyRule;
    private String resultCodeAttribute;
    private InitialContext initialContext_;
    private CompoundName ruleChainMgrName_;
    private RuleChainManager ruleChainMgr_ = null;
    private String ruleName_;
    private NMESchema nmeSchema_;

    @Override
    public void applyRule(NormalizedMeteredEvent nme, NormalizedMeteredEvent normalizedMeteredEvent1) throws RuleException {
        Attribute attr = nme.getAttribute("GR_INITED_FLAG");
        if (attr == null || attr.toString().equals("0")){
            //uninitialized
            setResultCode(nme, RESULT_CODE_NOT_INITED);
            handleRuleChain(uninitializedRule, nme);
            return;
        }
        attr = nme.getAttribute("GR_CURRENT_SITE_ID");
        if (attr == null){
            setResultCode(nme, RESULT_CODE_NOT_INITED);
            handleRuleChain(uninitializedRule, nme);
            return;
        }
        String siteId = attr.toString();

        if (notReadyRule != null) {
            int status = -1;

            CacheManager cm = EHCacheService.getCacheManager();
            Cache cache = cm.getCache("GR_SITE_STATUS");

            Element rs = cache.get(siteId);
            if (rs != null) {
                Map<String, String> map = (Map<String, String>) rs.getObjectValue();
                String str = map.get("GR_STATE");
                if (str != null) status = Integer.parseInt(str);
            }

            if (status == -1) {
                setResultCode(nme, RESULT_CODE_NO_STATUS_RECORD);
                handleRuleChain(notReadyRule, nme);
                return;
            }
        }

        if (unauthorizedRule != null){
            boolean authorized = true;
            String usr = null, pwd = null, cusr = null, cpwd = null;
            attr = nme.getAttribute(authorizationUserAttribute);
            if (attr == null){
                setResultCode(nme, RESULT_CODE_INVALID_INFO);
                authorized = false;
            } else {
                usr = attr.toString();
            }
            attr = nme.getAttribute(authorizationPasswordAttribute);
            if (attr == null){
                setResultCode(nme, RESULT_CODE_INVALID_INFO);
                authorized = false;
            } else {
                pwd = attr.toString();
            }
            if (authorized){
                attr = nme.getAttribute("GR_SMAN_USER");
                if (attr == null){
                    setResultCode(nme, RESULT_CODE_INVALID_SITE_INFO);
                    authorized = false;
                } else {
                    cusr = attr.toString();
                }
                attr = nme.getAttribute("GR_SMAN_PASSWORD");
                if (attr == null){
                    setResultCode(nme, RESULT_CODE_INVALID_SITE_INFO);
                    authorized = false;
                } else {
                    cpwd = attr.toString();
                }

                if (authorized){
                    authorized = cusr.equals(usr) && cpwd.equals(pwd);
                    if (!authorized){
                        setResultCode(nme, RESULT_CODE_LOGIN_FAILED);
                    }
                }
            }
            if (authorized){
                setResultCode(nme, 2001);
            } else {
                handleRuleChain(unauthorizedRule, nme);
                return;
            }
        }
        if (next_ != null) next_.applyRule(nme, normalizedMeteredEvent1);
    }

    private void setResultCode(NormalizedMeteredEvent nme, long code){
        Attribute attr = nme.getAttribute(resultCodeAttribute);
        if (attr == null) {
            attr = nmeSchema_.newAttributeInstance(resultCodeAttribute);
        }
        attr.setValue(String.valueOf(code));
        nme.setAttribute(attr, resultCodeAttribute);
    }

    @Override
    public void configure(Config config) throws ConfigurationException {
        try {
            nmeSchema_ = NMESchema.getInstance();

            Properties localProperties = new Properties();
            localProperties.put("jndi.syntax.direction", "left_to_right");
            localProperties.put("jndi.syntax.separator", "/");
            localProperties.put("jndi.syntax.ignorecase", "false");
            localProperties.put("jndi.syntax.escape", "\\");
            localProperties.put("jndi.syntax.beginquote", "'");
            localProperties.put("jndi.syntax.endquote", "'");
            localProperties.put("jndi.syntax.trimblanks", "false");

            this.initialContext_ = IUMContextHelper.createContext();
            this.ruleChainMgrName_ = new CompoundName(ServerInfo.getInstance().getServerName() + "/" + "RuleChainManager", localProperties);
            this.ruleName_ = config.getFullName();
        } catch (Exception e){
            throw new ConfigurationException(e.getMessage(), e);
        }

        unauthorizedRule = config.getAttributeAsString("UnauthorizedRule", null);
        if (unauthorizedRule != null) {
            authorizationUserAttribute = config.getAttributeAsString("AuthorizationUserAttribute");
            if (authorizationUserAttribute == null) throw new ConfigurationException("AuthorizationUserAttribute is mandatory.");
            authorizationPasswordAttribute = config.getAttributeAsString("AuthorizationPasswordAttribute");
            if (authorizationPasswordAttribute == null) throw new ConfigurationException("AuthorizationPasswordAttribute is mandatory.");
        }

        uninitializedRule = config.getAttributeAsString("UninitializedRule", null);
        if (uninitializedRule == null) throw new ConfigurationException("UninitializedRule is mandatory.");

        notReadyRule = config.getAttributeAsString("NotReadyRule", null);

        resultCodeAttribute = config.getAttributeAsString("ResultCodeAttribute", null);
        if (resultCodeAttribute == null) throw new ConfigurationException("ResultCodeAttribute is mandatory.");
    }

    private void handleRuleChain(String ruleChain, NormalizedMeteredEvent nme) throws RuleException {
        if (ruleChain != null)
        {
            if (this.ruleChainMgr_ == null) {
                try
                {
                    this.ruleChainMgr_ = ((RuleChainManager)this.initialContext_.lookup(this.ruleChainMgrName_));
                }
                catch (Exception localException)
                {
                    throw new RuleException("Session Rule " + this.ruleName_ + " threw exception in configured error rule chain " + ruleChain + " for NME: " + nme, localException);
                }
            }
            RuleChain localRuleChain = null;
            try
            {
                localRuleChain = this.ruleChainMgr_.getRuleChain(ruleChain);
                log_.logDebug3("Invoking rule chain " + ruleChain);
                if (localRuleChain == null) {
                    throw new RuleException("RuleChain " + ruleChain + " is not configured.");
                }
                localRuleChain.applyRule(nme, null);
            }
            finally
            {
                this.ruleChainMgr_.releaseRuleChain(ruleChain, localRuleChain);
            }
        }
    }
}
